#include "bobox_manager.hpp"
#include "bobox_results.hpp"
#include "../../../h/bofe_configuration.hpp"

#include "runtime.hpp"
#include "boxes.hpp"


#include "../source/runtime/Runtime.h"

//#include "boxes/input/HardCodedDataInputBoxDescriptor.h"
//#include "boxes/operation/AdditionBoxDescriptor.h"
//#include "boxes/output/ConsoleWriterBox.h"
//#include "boxes/output/ConsoleWriterBoxDescriptor.h"
//#include "boxes/TimerBox.h"
//#include "boxes/ForkBox.h"
//#include "boxes/MergeBox.h"
//
//#include "boxes/input/SqlDBInputBox.h"
//
////#include "model/modelGenerator/PlainParallelModelGenerator.h"

#include "genrators/ParallelArithmeticOperationModelGenerator.h"
#include "genrators/InputTestModel.h"
#include "genrators/TwoPhasesParrallelDBOperationModelGenerator.h"

class configuration : public bofe::configuration
{
		virtual std::string get_value(const std::string &name) const
		{
			return ".";
		}
};


std::string createModel(const int n, const int m, const size_t outSize) {

    const int parallelFactor = m*n;
    assert(n*m == parallelFactor);


    const int startBoxId = 1;
    const int endBoxId = 2;
    const int forkBoxId = 3;

    const char* endliner = "";//"\n";

    std::stringstream modelTxt;  
    modelTxt    << "box:" << startBoxId << "[input_box]()," << endliner
                << "box:" << endBoxId << "[output_box]()," << endliner
                << "box:" << forkBoxId << "[Fork](n=(" << parallelFactor <<"),)," << endliner;

    int boxId = 4;
        
    const int firstInputBoxId = boxId;

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            modelTxt << "box:"<< boxId++ << "[Input](" <<
                "n1=(" << n << "),n2=(" << m << "),i1=(" << i << "),i2=(" << j << "),)," << endliner;
            modelTxt << "box:" << boxId++ << "[Join](" <<
                "outSize=(" << outSize <<"),)," << endliner;
        }
    }

    const int outputBoxId = boxId;
    modelTxt << "box:" << boxId++ << "[Output]()," << endliner;

    int viaId = 1;
    // start -> fork
    modelTxt << "via:" << viaId++ << "["<< startBoxId <<":0]{}{}("<< forkBoxId <<":0),"  << endliner;

    for (size_t i = 0; i < parallelFactor; ++i) {
            
        // 2*i pretoze za InputBox vkladam JoinBox tj. InputBox je kazdy druhy
        const int inputBoxId = firstInputBoxId + 2*i;
        // fork -> input
        modelTxt << "via:" << viaId++ << "[" << forkBoxId << ":" << i << "]" 
            << "{}{}(" << inputBoxId << ":0)," << endliner;
            
        // prisluchajuci join box je hned za inputBoxm
        const int joinBoxId = inputBoxId + 1; 
        // input -> join : prva sada stlpcov
        modelTxt << "via:" << viaId++ << "[" << inputBoxId << ":0]" 
            << "{int(),int(),}{}(" << joinBoxId << ":0)," << endliner;

        // input -> join : druha sada stlpcov
        modelTxt << "via:" << viaId++ << "[" << inputBoxId << ":2]" 
            << "{int(),int(),}{}(" << joinBoxId << ":2)," << endliner;
        // stlpce nemozu ist spolocne pretoze mozu mat roznu dlzku

        // join -> output
        modelTxt << "via:" << viaId++ << "[" << joinBoxId << ":4]"
            << "{int(),int(),}{}(" << outputBoxId << ":0)," << endliner;
    }

        
    // output -> end
    modelTxt << "via:" << viaId++ << "[" << outputBoxId << ":0]"
        << "{}{}(" << endBoxId << ":0)," << endliner;

    std::string mm = modelTxt.str();
    return mm;
}


std::string getModels2() {
	return std::string("box:1[input_box](),box:2[output_box](),box:3[Cross](fork_ratio=(6),") +
		    "destinations=(2),),box:4[Hello](id=(1),),box:5[Hello](id=(2),)," +
            "via:1[1:0]{}{}(3:0),via:2[3:1]{}{}(4:0),via:3[3:2]{}{}(5:0),via:4[4:1]{}{}(2:0),via:5[5:1]{}{}(2:0),";
}

std::string getModel3() {

	return std::string("box:1[input_box](),box:2[output_box](),box:3[I](),box:4[O](),") +
		"via:1[1:0]{}{}(3:0),via:2[3:0]{int(),}{}(4:0),via:4[4:0]{}{}(2:0),";
}

std::string getModel4() {

	return std::string("box:1[input_box](),box:2[output_box](),box:3[I](),box:4[O](),box:5[O](),") +
		"via:1[1:0]{}{}(3:0),via:2[3:0]{int(),}{}(4:0),via:3[3:1]{int(),}{}(5:0),via:4[4:1]{}{}(2:0),via:5[5:1]{}{}(2:0),";
}

std::string getModel23() {

	return std::string("box:1[input_box](),box:2[output_box](),box:3[SI](),box:4[SA](),box:5[SO](),") +
		"via:1[1:0]{}{}(3:0),"+
		"via:2[3:0]{string(),}{}(4:0),via:3[3:1]{string(),}{}(4:1),via:4[3:2]{string(),}{}(4:2),"+
		"via:5[4:2]{string(),}{}(5:0),via:6[5:1]{}{}(2:0),";
}

std::string getModel2ee() {

	return std::string("box:1[input_box](),box:2[output_box](),box:3[II](),box:4[IA](),box:5[IO](),") +
		"via:1[1:0]{}{}(3:0),"+
		"via:2[3:0]{int(),}{}(4:0),via:3[3:1]{int(),}{}(4:1),via:4[3:2]{int(),}{}(4:2),"+
		"via:5[4:2]{int(),}{}(5:0),via:6[5:1]{}{}(2:0),";
}

std::string getModel2t() {

	return std::string("box:1[input_box](),box:2[output_box](),box:3[II](),box:4[IA](),box:5[IO](),") +
		"via:1[1:0]{}{}(3:0),"+//input_box->II
		"via:2[3:0]{int(),int(),int(),}{}(4:0),"+//II->IA
		//"via:3[3:1]{int(),}{}(4:1),via:4[3:2]{int(),}{}(4:2),"+
		"via:3[4:0]{int(),}{}(5:0),"+//IA->IO
		"via:4[5:1]{}{}(2:0),";//IO->output_box
}


std::string getModel222() {

	return std::string("box:1[input_box](),box:2[output_box](),box:3[II](),")+
		"box:4[Fork](forkFactor=(2),),box:5[IA](),box:6[IA](),box:7[IO](),box:8[IO]()," +
		"via:1[1:0]{}{}(3:0),"+//input_box->II
		"via:2[3:0]{int(),int(),int(),}{}(4:0),"+//II->Fork
		"via:3[4:0]{int(),int(),int(),}{}(5:0),"+//Fork->IA
		"via:4[4:1]{int(),int(),int(),}{}(6:0),"+//Fork->IA
		"via:5[5:0]{int(),}{}(7:0),"+//IA->IO
		"via:6[6:0]{int(),}{}(8:0),"+//IA=>IO
		"via:7[7:1]{}{}(2:0),"+//IO->output_box
		"via:8[8:1]{}{}(2:0),";//IO->output_box
}
std::string getModel2() {
	//return std::string("box:1[input_box](),box:2[output_box](),")+
	//	"box:3[Fork](forkFactor=(2),),box:4[InBox](type=(3),),box:5[InBox](type=(2),),"+
	//	"box:6[OutBox](),box:7[OutBox](),box:8[Merge](mergeFactor=(2),)," +
	//	"via:1[1:0]{}{}(3:1),"+//input_box->Fork
	//	"via:2[3:0]{}{}(4:0),"+//Fork-IN1
	//	"via:3[3:1]{}{}(5:0),"+//Fork-IN2
	//	"via:3[4:0]{int(),}{}(6:0),"+//IN1->O1
	//	"via:4[4:1]{int(),}{}(7:0),"+//IN1->O2
	//	"via:5[5:0]{int(),}{}(6:1),"+//IN2->O1
	//	"via:6[5:1]{int(),}{}(7:1),"+//IN2->O2
	//	"via:7[6:0]{}{}(8:0),"+//O1->merge
	//	"via:7[7:0]{}{}(8:1),"+//O2->merge
	//	"via:7[8:0]{}{}(2:0),";//merge->output_box


	using namespace jm4bbx;

	//SqlDBInputBox x;
	//x.foo();

	//TestModel<1, HardCodedIntegerDataInputBoxDescriptor, ForkBoxDescriptor, 
	//	IntegerAdditionBoxDescriptor, IntegerConsoleWriterBoxDescriptor, MergeBoxDescriptor> amg2; 
	
	//PlainParrallelModelGenerator<4, 
	//	HardCodedStringDataInputBoxDescriptor, 
	//	ForkBoxDescriptor, 
	//	StringAdditionBoxDescriptor, 
	//	StringConsoleWriterBoxDescriptor, 
	//	MergeBoxDescriptor, 
	//	TimerBoxDescriptor> amg; 

//################
	
	//InputTestModel amg("SQL_INTEGER", "IO", "int", 2);
	//InputTestModel amg("SQL_STRING", "SO", "string", 2);

	//ParallelArithmeticOperationModelGenerator<2> amg("SQL_STRING", "ADDITION_STRING","SO", "string");	
	//ParallelArithmeticOperationModelGenerator<2> amg("SQL_INTEGER", "ADDITION_INTEGER","IO", "int");


	//TwoPhasesParrallelHashActionModelGenerator<3> amg("SQL_STRING", "HASH_STRING", "HASH_INTERSECTION_STRING", "SO", "string");
	//TwoPhasesParrallelHashActionModelGenerator<3> amg("SQL_INTEGER", "HASH_INTEGER", "HASH_INTERSECTION_INTEGER", "IO", "int");

	TwoPhasesParrallelHashActionModelGenerator<2> amg("II", "A", "B",
		"HASH_INTEGER", "HASH_JOIN_INTEGER", "SO", "int");
	//TwoPhasesParrallelHashActionModelGenerator<2> amg("SQL_INTEGER", "HASH_INTEGER", "HASH_JOIN_INTEGER", "IO", "int");
	std::cout << amg.generateModel() << std::endl;



	//std::cout << amg.generateModel() << std::endl;

	return amg.generateModel();
	//return getModel222();
}


std::string getModel212() {
	

	return std::string("box:1[input_box](),box:2[output_box](),box:3[II](),")+
		"box:4[Fork](forkFactor=(3),),box:5[IA](),box:6[IA](),box:7[IA](),"+
		"box:8[IO](),box:9[IO](),box:10[IO](),box:11[Merge](mergeFactor=(3),)," +
		"via:1[1:0]{}{}(3:0),"+//input_box->II
		"via:2[3:0]{int(),int(),int(),}{}(4:0),"+//II->Fork
		"via:3[4:0]{int(),int(),int(),}{}(5:0),"+//Fork->IA
		"via:4[4:1]{int(),int(),int(),}{}(6:0),"+//Fork->IA
		"via:5[4:2]{int(),int(),int(),}{}(7:0),"+//Fork->IA
		"via:5[5:0]{int(),}{}(8:0),"+//IA->IO
		"via:6[6:0]{int(),}{}(9:0),"+//IA=>IO
		"via:6[7:0]{int(),}{}(10:0),"+//IA=>IO
		"via:7[8:0]{}{}(11:0),"+//IO->merge
		"via:8[9:0]{}{}(11:1),"+//IO->merge
		"via:8[10:0]{}{}(11:2),"+//IO->merge
		"via:9[11:0]{}{}(2:0),";//merge->output_box
}



void test(jm4bbx::runtime& rt, bobox::manager& mng, const int n, const int m, const size_t outSize) {

    for (size_t i = 0; i < 10; ++i) {
		std::string txtModel = createModel(n, m, outSize);
		if (i==0) {
			std::cout << txtModel << std::endl << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl << std::flush;
		}
		continue;
        bobox::request_id_type rqid = mng.create_request(txtModel, &rt);
        ulibpp::uUINT64 start = ulibpp::system_info::get_relative_time();
        mng.run_request(rqid);
	    mng.wait_on_request(rqid);	
        ulibpp::uUINT64 delay = ulibpp::system_info::get_relative_time() - start;
        std::cout << n << "," << m << "," << outSize << "," << delay << std::endl;


        switch (mng.get_result(rqid)) {
	        case bobox::RR_ERROR:
		        std::cout << "Error" << std::endl;
		        break;
	        case bobox::RR_CANCELED:
		        std::cout << "Canceled" << std::endl;
		        break;
	        case bobox::RR_DEADLOCK:
		        std::cout << "Deadlock" << std::endl;
		        break;
	        case bobox::RR_MEMORY:
		        std::cout << "Memory" << std::endl;
		        break;
	        case bobox::RR_OK:
//		        std::cout << "OK" << std::endl;
		        break;
	        case bobox::RR_TIMEOUT:
		        std::cout << "Timeout" << std::endl;
		        break;
	        default:
		        assert(false);
	        }
        mng.destroy_request(rqid);
    }
    
}


int main(int argc, char *argv[])
{


	bobox::manager mng(true);
	configuration conf;

	jm4bbx::runtime rt;
	rt.init(conf);
    
    //rt.createTables(100, 10);

    //ulibpp::uUINT64 start = ulibpp::system_info::get_relative_time();
    //    
    //for (size_t n = 1; n < 16; n*=2) {
    //    for (size_t m = 1; m < 16; m*=2) {
    //        //test(rt, mng, n, m, 0);
    //        for (size_t i = 1; i < 100000; i*=100) {
    //            test(rt, mng, n, m, i);
    //        }
    //    }
    //}

	bobox::request_id_type rqid = mng.create_request(
		getModel2(),
		//std::string("box:1[input_box](),")+
		//"box:2[output_box](),"+
		//"box:3[FlowBox](),"+
		//"via:1[1:0]{}{}(3:0),"+
		//"via:2[3:0]{}{}(2:0),",
		&rt);

		mng.run_request(rqid);
		mng.wait_on_request(rqid);	
	

        //    bobox::request_id_type rqid = mng.create_request(
        //std::string("box:1[input_box](),")+
        //"box:2[output_box](),"+
        //"box:3[Fork](),"+
        //"box:4[Input](n1=(2),n2=(1),i1=(0),i2=(0),),"+
        //"box:5[Input](n1=(2),n2=(1),i1=(1),i2=(0),),"+
        //"box:6[Join](),"+
        //"box:7[Join](),"+
        //"box:8[Output](),"+
        ////"box:9[Output](),"+
        //"via:1[1:0]"+ // init -> fork
        //    "{}"+"{}"+"(3:0),"+
        //"via:2[3:0]"+ // fork -> input1
        //    "{}"+"{}"+"(4:0),"+
        //"via:3[3:1]"+ // fork -> input2
        //    "{}"+"{}"+"(5:0),"+
        //"via:4[4:0]"+ // input1 -> join1
        //    "{int(),int(),}{}(6:0),"+
        //"via:5[4:2]"+ // input1 -> join1
        //    "{int(),int(),}{}(6:2),"+
        //"via:4[5:0]"+ // input2 -> join2
        //    "{int(),int(),}{}(7:0),"+
        //"via:5[5:2]"+ // input2 -> join2
        //    "{int(),int(),}{}(7:2),"+
        //"via:6[6:4]"+ // join1 -> output1
        //    "{int(),int(),}{}(8:0),"+
        //"via:7[7:4]"+ // join2 -> output2
        //    "{int(),int(),}{}(8:0),"+
        //"via:8[8:0]"+ // output1 -> end
        //    "{}"+"{}"+"(2:0),",
        ////"via:9[9:0]"+ // output2 -> end
        ////    "{}"+"{}"+"(2:0),",

        // &rt);




	const bobox::results *results = mng.get_results(rqid);

	//for (size_t i=0; i<results->get_tables_count(); i++) {
	//	if (i == 1) {
	//		std::cout << i << " : " << results->get_rows_count(i) << std::endl;
	//		std::cout << "Prvnich par vysledku:" << std::endl;
	//	}
	//	size_t n = 0;
	//	for (size_t j=0; j<results->get_rows_count(i); j++, n++) {
	//		bobox::results::rows_type rows;
	//		results->get_rows(i, j, 1, rows);

	//		for (size_t k=0; k<rows[0].size(); k++) {
	//			bobox::results::rows_type labels;
	//			results->get_rows(0, 0, 1, labels);
	//			std::cout << labels[0][k] << ": " << rows[0][k] << std::endl;
	//		}
	//		std::cout << std::endl;
	//	}
	//}





	//return 0;
}
